#include "bsw_hash.h"

struct bsw_hash {
    bsw_hash_f hash;
    bsw_startof_f startof;
    bsw_compare_f compare;
    
    int size, idx;
    bsw_list_node_s *each;
    bsw_list_node_s list[];
};

bsw_hash_s * bsw_hash_init(bsw_hash_app_s *app)
{
    int i;
    bsw_hash_s *h;
    int size = app->size;

    size = sizeof(bsw_hash_s)+ size*(sizeof(bsw_list_node_s));
    h = (bsw_hash_s *)malloc(size);
    if (!h)
        return h;
    h->hash    = app->hash;
    h->startof = app->startof;
    h->compare = app->compare;
    h->size    = app->size;

    h->idx = 0;
    h->each = h->list;
    for (i = 0; i < h->size; ++i)
        bsw_list_init(h->list+i);
    return h;
}

void bsw_hash_close(bsw_hash_s *h)
{
    free(h);
    return ;
}

int bsw_hash_size(bsw_hash_s *h)
{
    return h->size;
}

void bsw_hash_add(bsw_hash_s *h, bsw_list_node_s *n, void *k)
{
    bsw_list_node_s *head;

    head = h->list + h->hash(k);
    bsw_list_add(n, head);
    return ;
}

void * bsw_hash_find(bsw_hash_s *h, void *k)
{
    void *d;
    bsw_list_node_s *n, *head;

    head = h->list + h->hash(k);
    bsw_list_foreach(n, head) {
        d = h->startof(n);
        if (h->compare(d, k))
            continue;
        return d;
    }
    return NULL;
}

void * bsw_hash_del (bsw_hash_s *h, void *k)
{
    void *d;
    bsw_list_node_s *n, *p, *head;

    head = h->list + h->hash(k);
    bsw_list_foreach_safe(n, p, head) {
        d = h->startof(n);
        if (h->compare(d, k))
            continue;
        bsw_list_del(n, p);
        return d;
    }
    return NULL;
}

/**
 * 依次遍历在哈希表 @h 中的各个元素
 */
void * bsw_hash_each(bsw_hash_s *h)
{
    void *d;
    bsw_list_node_s *e;
    int size = h->size;

    if (h->idx >= size)
        return NULL;
    e = h->list + h->idx;
    if (e != h->each)
        e = h->each;
    else
        e = e->next;

    for (;;) {
        if (e) {
            h->each = e->next;
            d = h->startof(e);
            return d;
        }
        h->idx += 1;
        if (h->idx >= size)
            break;
        e = h->list + h->idx;
        e = e->next;
    }
    return NULL;
}

/**
 * 遍历哈希表 @h 并且将所遍历结点从哈希表中删除
 * 返回所遍历目标元素
 */
void * bsw_hash_each_del(bsw_hash_s *h)
{
    void *d;
    bsw_list_node_s *n, *p, *head;
    int size = h->size;

    if (h->idx >= size)
        return NULL;
    for (;;) {
        head = h->list + h->idx;
        bsw_list_foreach_safe(n, p, head) {
            bsw_list_del(n, p);
            d = h->startof(n);
            return d;
        }
        h->idx += 1;
        if (h->idx >= size)
            break;
        head = h->list + h->idx;
    }
    return NULL;
}

/**
 * 重置哈希表 @h 以便从头开始遍历
 */
void bsw_hash_each_reset(bsw_hash_s *h)
{
    h->idx = 0;
    h->each = h->list;
    return ;
}